package org.example._05_chat;


import java.io.IOException;
import java.net.*;
import java.io.*;
import java.util.*;//数组包

//本板块中基本实现了两个用户的交互，本板块的缺陷是只有当两个用户在线的时候才能进行交互。
//当一个用户不再的时候就只能把该用户的数据传输到服务器。
//当服务器接收到数据的时候，再将数据传输到其他用户。
public class ChatServer {
    ServerSocket ss =null;
    boolean started = false;

    List<Client> clients = new ArrayList<Client>();

    public static void main(String[] args) {
        new ChatServer().start();
    }

    private void start (){
        try {
            ss =new ServerSocket(8888);//ServerSocket(int port)   创建绑定到特定端口的服务器套接字。
        }catch(BindException e){  //防止在服务器没启用前，就有客户端的连接。
            System.out.println("端口使用中。。。。");
            System.out.println("请关闭相关程序并重新启动");
            System.exit(0);
        }catch(Exception e){
            e.printStackTrace();
        }try{
            started = true;
            while(started){
                boolean beConnect = false;
                Socket s = ss.accept();//在Server中的方法：accept() 侦听并接受到此套接字的连接。    Socker是一个实现客户端套接字的类。
                System.out.println("a client connected!");//打印出来这句话的时候，说明客户端电源已经开启，接下来就用线程来控制连接。
                Client c = new 	Client(s);
                clients.add(c);
                new Thread(c).start(); //创建线程并运行调用run方法。
            }}catch(IOException e){
            e.printStackTrace();
        }finally{
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class Client implements Runnable{  //此处用接口的形式创建了一个线程，而不用继承。因为是多个对象调用时，就用接口方式暴露出来。
        private Socket s =null;
        private DataInputStream dis= null;
        private DataOutputStream dos =null;
        private boolean beConnect = false;

        public Client(Socket s){
            this.s=s;
            try {
                dis = new DataInputStream(s.getInputStream());
                dos = new DataOutputStream(s.getOutputStream());
                beConnect = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void run() {
            try {
                while(beConnect){
                    String str = dis.readUTF();//读进一个数据。//此数据输入流readUTF（）方法是与ChatClient类中的数据输出流是相互呼应的！
                    System.out.println(str);//回顾到0.4版本中的问题，在这里做出解释，是readUTF（）中一直在等待数据过来，而关闭窗口时，
                    //Sockeet 还没有close掉，还在指向客户端的套接字。所以应在readUTF读写的时候做异常处理。
                    for(int x=0;x<clients.size();x++){//遍历集合中的每一个元素。｛只要x小于这个集合中的数量就循环｝
                        Client c = clients.get(x);//用get（）方法获得其中一个元素，
                        c.send(str);//调用本类中的send发送方法。
                    }
                } } catch(EOFException e){
                System.out.println("Client closed !");
            } catch (IOException e) {    //在检测到有任何的异常，在此直针对readUTF()中的异常，做出相应的处理。
                e.printStackTrace();
            } finally{
                try {
                    if(s!=null) s.close();
                    if(dis!=null) dis.close();
                    if(dos!=null)dos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        }
        public void send(String str){
            try {
                dos.writeUTF(str);    //写出一个数据，用于用户端口的接受。
            } catch(SocketException e){
                clients.remove(this);
//				e.printStackTrace();
                System.out.println("对方退出了，我从list中移除了！");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}







//在这个版本中基本实现了一个用户对server服务端的连接。需要提醒的是这个版本并没有实现两个用户的交互。
